package com.ghc.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;

@Configuration
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private GhcAccessTokenConvertor ghcAccessTokenConvertor;

    /**
     * 认证服务器是以api接口的方式对外提供服务， 校验合法性并生成令牌， 校验令牌
     * 那么以api接口方式的话，就涉及到接口访问权限， 我们需要在这里进行必要的配置
     * @param security 验证
     * @throws Exception 异常
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        security
                //允许客户端表单认证
                .allowFormAuthenticationForClients()
                // 开启端口 /oauth/token_key 的访问权限（允许）
                .tokenKeyAccess("permitAll()")
                //开启端口 /oauth/check_token的访问权限 （允许）
                .checkTokenAccess("permitAll()");
    }


    /**
     * 客户端详情配置 比如 client_id, secret
     * 当前这个服务就如同QQ。， 拉钩网作为客户端需要qq平台进行登录授权认证， 提前需要到QQ平台注册， QQ会给拉勾网
     * 办法client_id等必要的参数， 表明客户端是谁
     * @param clients 客户端
     * @throws Exception 异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);

        /*clients.inMemory() //客户端信息储存在什么地方 可以在内存中， 也可以在数据库中
                .withClient("client_ghc") //添加一个client配置 指定client_id
                .secret("abcdefg") //指定客户端的密码、安全吗
                .resourceIds("autodeliver") //指定客户端能访问资源的id清单， 资源名跟资源服务器配置相同
                //认证类型。令牌办法模式， 可以配置多个， 但不一定都用
                .authorizedGrantTypes("password", "refresh_token")
                //客户端的权限范围
                .scopes("all");*/
        clients.withClientDetails(createJdbcClientDetailsService());
    }

    @Autowired
    private DataSource dataSource;

    @Bean
    public JdbcClientDetailsService createJdbcClientDetailsService(){
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * 认证服务器是玩转token的， 配置token令牌管理相关 （字符串， 当下）
     * @param endpoints 令牌
     * @throws Exception 异常
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints.tokenStore(tokenStore()) //指定token储存方法
                .tokenServices(authorizationServerTokenServices()) // token服务的一个描述， 可以认为是token生成细节的描述
                .authenticationManager(authenticationManager) //指定认证管理器 随后注入到当前的类使用即可
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    }

    /**
     * 该方法用于床架tokenStore对象 （令牌储存对象）
     token以什么形式储存
     */
    public TokenStore tokenStore(){
//        return new InMemoryTokenStore();
        //使用jwt令牌储存数据
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    private final String sign_key = "ghc123";

    /**
     * 返回juw令牌转换器， （帮助我们生成jwt令牌）
     * 在这里我们可以把签名秘钥传进去给转换器对象
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey(sign_key);   //签名秘钥
        jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key));   //验证时使用的秘钥， 和签名秘钥保持一致
        jwtAccessTokenConverter.setAccessTokenConverter(ghcAccessTokenConvertor); //自定义参数到jwt
        return jwtAccessTokenConverter;
    }


    /**
     * 该方法用户获取一个token服务对象， 描述了token的有效期等信息
     */
    public AuthorizationServerTokenServices authorizationServerTokenServices(){
        //使用默认试下
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setSupportRefreshToken(true); //是否开启令牌刷新
        defaultTokenServices.setTokenStore(tokenStore());

        //针对jwt令牌添加增强
        defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());


        //设置令牌有效时间（一般2小时）  access_token就是我们请求资源需要携带的令牌
        defaultTokenServices.setAccessTokenValiditySeconds(20);
        //设置刷新令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds(259200); //3天
        return defaultTokenServices;

    }
}


